package me.frank.tman.presenter;

import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Log;

import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonWriter;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import me.frank.tman.model.AnalyseModel;
import me.frank.tman.model.StockExchangeModel;
import me.frank.tman.model.StockModel;

public class StockPresenter {

    private static final String TAG = StockPresenter.class.getSimpleName();

    private static final boolean DBG = Log.isLoggable(TAG, Log.VERBOSE);

    private Context mContext;
    private Handler mHandler;
    private File mStockFile;

    private List<StockModel> mStocks;

    private List<StockModel> mLiteStocks = null;

    private List<String> mStockCodes = new ArrayList<>();
    private List<String> mStockNames = new ArrayList<>();

    private Map<String, List<StockExchangeModel>> mExchangeRecordsMap = new HashMap<>();

    private static StockPresenter mInstance = null;

    public static void init(Context context) {
        if (mInstance == null) {
            mInstance = new StockPresenter(context);
        }
    }

    public static StockPresenter get() {
        return mInstance;
    }

    private StockPresenter(Context context) {
        mContext = context;
        mStockFile = new File(mContext.getFilesDir(), "stock.json");

        HandlerThread thread = new HandlerThread("load");
        thread.start();
        mHandler = new Handler(thread.getLooper());
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                List<StockModel> stocks = loadLocalStocks();

                if (stocks == null) {
                    stocks = new ArrayList<>();
                }
                mStocks = stocks;

                mLiteStocks = loadStockLiteModels();

                if (mLiteStocks != null) {
                    for (StockModel stock : mLiteStocks) {
                        mStockCodes.add(String.format("%s\t%s", stock.code.substring(3), stock.code_name));
                        mStockNames.add(String.format("%s\t%s", stock.code_name, stock.code.substring(3)));
                    }
                }
            }
        });
    }

    public List<String> getStockCodes() {
        return mStockCodes;
    }

    public List<String> getStockNames() {
        return mStockNames;
    }

    public Handler getWorkHandler() {
        return mHandler;
    }

    ////////////////////////////STOCK/////////////////////////////////////////////
    private List<StockModel> loadStockLiteModels() {
        InputStream inputStream = null;
        try {
            inputStream = mContext.getAssets().open("all_stock.json");
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

        Type collectionType = new TypeToken<List<StockModel>>() {
        }.getType();
        List<StockModel> stocks = new GsonBuilder().create().fromJson(new InputStreamReader(inputStream), collectionType);

//        for (StockModel stock : stocks) {
//            Log.d(TAG, stock.toString());
//        }

        return stocks;
    }

    public List<StockModel> getLiteStocks() {
        return mLiteStocks;
    }

    private List<StockModel> loadLocalStocks() {
        if (!mStockFile.exists()) {
            Log.e(TAG, "local stock file is not exist .");
            return null;
        }

        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(mStockFile);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

        Type collectionType = new TypeToken<List<StockModel>>() {
        }.getType();
        List<StockModel> stocks = new GsonBuilder().create().fromJson(new InputStreamReader(inputStream), collectionType);

        if (DBG) {
            for (StockModel stock : stocks) {
                Log.d(TAG, stock.toString());
            }
        }

        return stocks;
    }

    public boolean isStockExist(StockModel stock) {
        if (mStocks.size() > 0) {
            for (StockModel s : mStocks) {
                if (s.code.equals(stock.code)) {
                    Log.i(TAG, stock.code_name + " already exist!");
                    return true;
                }
            }
            return false;
        }
        return false;
    }

    public StockModel getStock(StockModel stock) {
        if (mStocks.size() > 0) {
            for (StockModel s : mStocks) {
                if (s.code.equals(stock.code)) {
                    Log.i(TAG, stock.code_name + " found!");
                    return s;
                }
            }
            return null;
        }
        return null;
    }

    public boolean writeStock(StockModel stock) {
        if (isStockExist(stock)) {
            StockModel s = getStock(stock);
        } else {
            mStocks.add(stock);
        }

        try {
            if (mStockFile.exists()) {
                mStockFile.delete();
            }
            FileOutputStream fos = new FileOutputStream(mStockFile);
            JsonWriter writer = new JsonWriter(new OutputStreamWriter(fos, "utf-8"));
            writer.setIndent("    ");
            writer.beginArray();
            for (StockModel s : mStocks) {
                Log.d(TAG, stock.toString());
                writer.beginObject();
                writer.name("code").value(s.code);
                writer.name("code_name").value(s.code_name);
                writer.endObject();
            }
            writer.endArray();
            writer.close();
            return true;
        } catch (FileNotFoundException | UnsupportedEncodingException e) {
            Log.e(TAG, e.getMessage());
        } catch (IOException e) {
            Log.e(TAG, e.getMessage());
        }
        return false;
    }

    public boolean saveStock() {
        try {
            if (mStockFile.exists()) {
                mStockFile.delete();
            }
            FileOutputStream fos = new FileOutputStream(mStockFile);
            JsonWriter writer = new JsonWriter(new OutputStreamWriter(fos, "utf-8"));
            writer.setIndent("    ");
            writer.beginArray();
            for (StockModel s : mStocks) {
                Log.d(TAG, s.toString());
                writer.beginObject();
                writer.name("code").value(s.code);
                writer.name("code_name").value(s.code_name);
                writer.endObject();
            }
            writer.endArray();
            writer.close();
            return true;
        } catch (FileNotFoundException | UnsupportedEncodingException e) {
            Log.e(TAG, e.getMessage());
        } catch (IOException e) {
            Log.e(TAG, e.getMessage());
        }
        return false;
    }

    public List<StockModel> getStocks() {
        return mStocks;
    }

    ////////////////////////////STOCK EXCHANGE/////////////////////////////////////////////
    private File getStockExchangeFile(String stockCode) {
        return new File(mContext.getFilesDir(), stockCode + ".json");
    }

    public List<StockExchangeModel> loadStockExchangeRecords(String stockCode) {
        File exchangeFile = getStockExchangeFile(stockCode);
        if (!exchangeFile.exists()) {
            Log.e(TAG, stockCode + " exchange file is not exist .");
            return null;
        }

        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(getStockExchangeFile(stockCode));
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

        Type collectionType = new TypeToken<List<StockExchangeModel>>() {
        }.getType();
        List<StockExchangeModel> records = new GsonBuilder().create().fromJson(new InputStreamReader(inputStream), collectionType);

        if (DBG) {
            for (StockExchangeModel stock : records) {
                Log.d(TAG, stock.toString());
            }
        }

        return records;
    }

    public List<StockExchangeModel> getLatestStockExchanges() {
        List<StockModel> stocks = getStocks();

        if (stocks == null || stocks.size() <= 0) {
            return null;
        }

        List<StockExchangeModel> records = new ArrayList<>();
        List<StockModel> toBeDelete = new ArrayList<>();

        for (StockModel s : stocks) {
            StockExchangeModel exchangeModel = getLatestExchangRecord(s.code);
            if (exchangeModel != null) {
                records.add(exchangeModel);
            } else {
                toBeDelete.add(s);
            }
        }

        for (StockModel s : toBeDelete) {
            stocks.remove(s);
            File f = getStockExchangeFile(s.code);
            if (f.exists()) {
                f.delete();
            }
        }

        mStocks = stocks;
        saveStock();

        return records;
    }

    public List<StockExchangeModel> getStockExchanges(String code) {
        return loadStockExchangeRecords(code);
    }

    public boolean deleteExhangeRecord(StockExchangeModel record) {
        if (record == null) {
            return false;
        }

        List<StockExchangeModel> records = mExchangeRecordsMap.get(record.code);

        if (records == null || records.size() <= 0) {
            records = loadStockExchangeRecords(record.code);
        }

        if (records == null || records.size() <= 0) {
            return false;
        } else {
            StockExchangeModel target = null;
            for (StockExchangeModel r : records) {

                if ((r.exchangeTime == record.exchangeTime) && (r.code.equals(record.code))) {
                    target = r;
                    break;
                }
            }

            if (target != null) {
                records.remove(target);
                mExchangeRecordsMap.put(record.code, records);
                saveExchangeRecords(record.code);
                return true;
            }
        }
        return false;
    }

    public StockExchangeModel getLatestExchangRecord(String stockCode) {
        List<StockExchangeModel> records = mExchangeRecordsMap.get(stockCode);

        if (records == null || records.size() <= 0) {
            records = loadStockExchangeRecords(stockCode);
        }

        if (records == null || records.size() <= 0) {
            return null;
        } else {
            long timestamp = -1;
            StockExchangeModel record = null;
            for (StockExchangeModel r : records) {
                long exchangeTime = r.exchangeTime;
                if (exchangeTime > timestamp) {
                    timestamp = exchangeTime;
                    record = r;
                }
            }
            return record;
        }
    }

    public boolean saveExchangeRecords(String stockCode) {
        if (TextUtils.isEmpty(stockCode)) {
            Log.e(TAG, "stockCode can't be null !");
            return false;
        }
        List<StockExchangeModel> records = mExchangeRecordsMap.get(stockCode);

        try {
            File recordFile = getStockExchangeFile(stockCode);
            if (recordFile.exists()) {
                recordFile.delete();
            }
            FileOutputStream fos = new FileOutputStream(recordFile);
            JsonWriter writer = new JsonWriter(new OutputStreamWriter(fos, "utf-8"));
            writer.setIndent("    ");
            writer.beginArray();
            for (StockExchangeModel r : records) {
                writer.beginObject();
                writer.name("code").value(r.code);
                writer.name("name").value(r.name);
                writer.name("price").value(r.price);
                writer.name("exchangeType").value(r.exchangeType);
                writer.name("exchangeNum").value(r.exchangeNum);
                writer.name("volume").value(r.volume);
                writer.name("cost").value(r.cost);
                writer.name("capitalization").value(r.capitalization);
                writer.name("profit").value(r.profit);
                writer.name("exchangeFee").value(r.exchangeFee);
                writer.name("exchangeTime").value(r.exchangeTime);
                writer.endObject();
            }
            writer.endArray();
            writer.close();
            return true;
        } catch (FileNotFoundException | UnsupportedEncodingException e) {
            Log.e(TAG, e.getMessage());
        } catch (IOException e) {
            Log.e(TAG, e.getMessage());
        }
        return false;
    }

    public boolean writeStockExchangeRecord(StockExchangeModel record) {
        if (record == null) {
            Log.e(TAG, "record can't be null !");
            return false;
        }
        List<StockExchangeModel> records = mExchangeRecordsMap.get(record.code);

        if (records == null || records.size() <= 0) {
            records = loadStockExchangeRecords(record.code);
        }

        if (records == null) {
            records = new ArrayList<>();
            writeStock(new StockModel(record.code, record.name));
        }

        records.add(record);
        mExchangeRecordsMap.put(record.code, records);

        try {
            File recordFile = getStockExchangeFile(record.code);
            if (recordFile.exists()) {
                recordFile.delete();
            }
            FileOutputStream fos = new FileOutputStream(recordFile);
            JsonWriter writer = new JsonWriter(new OutputStreamWriter(fos, "utf-8"));
            writer.setIndent("    ");
            writer.beginArray();
            for (StockExchangeModel r : records) {
                writer.beginObject();
                writer.name("code").value(r.code);
                writer.name("name").value(r.name);
                writer.name("price").value(r.price);
                writer.name("exchangeType").value(r.exchangeType);
                writer.name("exchangeNum").value(r.exchangeNum);
                writer.name("volume").value(r.volume);
                writer.name("cost").value(r.cost);
                writer.name("capitalization").value(r.capitalization);
                writer.name("profit").value(r.profit);
                writer.name("exchangeFee").value(r.exchangeFee);
                writer.name("exchangeTime").value(r.exchangeTime);
                writer.endObject();
            }
            writer.endArray();
            writer.close();
            return true;
        } catch (FileNotFoundException | UnsupportedEncodingException e) {
            Log.e(TAG, e.getMessage());
        } catch (IOException e) {
            Log.e(TAG, e.getMessage());
        }
        return false;
    }


    private double getFeeOfBuy(String stockCode, double stockPrice, int stockVolume) {
        double turnover = stockPrice * stockVolume; // 成交额
        double transferFee = turnover * 0.00002; // 过户费，深交所的包含在规费里面，不单独收取
        double commission = turnover * 0.00025; // 佣金
        double exchangeFee = turnover * 0.0000687; // 交易规费包含万分之0.2的证管费和万分之0.487的交易经手费

        double totalFee = 0;

        if (stockCode.startsWith("6")) {
            if ((commission + exchangeFee) < 5) {
                totalFee = 5 + transferFee;
            } else {
                totalFee = commission + exchangeFee + transferFee;
            }
        } else if (stockCode.startsWith("00")) {
            if ((commission + exchangeFee) < 5) {
                totalFee = 5;
            } else {
                totalFee = commission + exchangeFee;
            }
        }
        return totalFee;
    }

    private double getFeeOfSell(String stockCode, double stockPrice, int stockVolume) {
        double turnover = stockPrice * stockVolume; // 成交额
        double stampDutyFee = turnover * 0.001; // 印花税
        double transferFee = turnover * 0.00002; // 过户费，深交所的包含在规费里面，不单独收取
        double commission = turnover * 0.00025; // 佣金
        double exchangeFee = turnover * 0.0000687; // 交易规费包含万分之0.2的证管费和万分之0.487的交易经手费

        double totalFee = 0;

        if (stockCode.startsWith("6")) {
            if ((commission + exchangeFee) < 5) {
                totalFee = 5 + transferFee + stampDutyFee;
            } else {
                totalFee = commission + exchangeFee + transferFee + stampDutyFee;
            }
        } else if (stockCode.startsWith("00")) {
            if ((commission + exchangeFee) < 5) {
                totalFee = 5 + stampDutyFee;
            } else {
                totalFee = commission + exchangeFee + stampDutyFee;
            }
        }
        return totalFee;
    }

    public boolean setup(String stockCode, String stockName, double stockPrice, double stockCost, int stockVolume) {
        StockExchangeModel record = getLatestExchangRecord(stockCode);
        if (record != null) {
            Log.e(TAG, stockName + " already setup !");
            return false;
        }

        record = new StockExchangeModel(stockCode, stockName, stockPrice, stockCost);

        record.exchangeNum = stockVolume;
        record.exchangeFee = getFeeOfBuy(stockCode, stockCost, stockVolume);
        record.volume = stockVolume;
        record.capitalization = record.price * record.volume;
        record.profit = record.price * record.volume - record.cost * record.volume;
        record.exchangeType = StockExchangeModel.BUY;
        record.exchangeTime = System.currentTimeMillis();

        Log.d(TAG, "setup: record=" + record);

        return writeStockExchangeRecord(record);
    }


    public StockExchangeModel buy(String stockCode, String stockName, double stockPrice, int stockVolume) {
        StockExchangeModel record = getLatestExchangRecord(stockCode);
        if (record == null) {
            record = new StockExchangeModel(stockCode, stockName, stockPrice);
        } else {
            record = new StockExchangeModel(record);
        }
        Log.d(TAG, "buy: latest=" + record);
        record.exchangeNum = stockVolume;
        record.exchangeFee = getFeeOfBuy(stockCode, stockPrice, stockVolume);
        record.cost = (record.cost * record.volume + stockPrice * stockVolume + record.exchangeFee) / (record.volume + stockVolume);
        record.volume += stockVolume;
        record.price = stockPrice;
        record.capitalization = record.price * record.volume;
        record.profit = record.price * record.volume - record.cost * record.volume;
        record.exchangeType = StockExchangeModel.BUY;
        record.exchangeTime = System.currentTimeMillis();

        writeStockExchangeRecord(record);
        return record;
    }

    public StockExchangeModel sell(String stockCode, String stockName, double stockPrice, int stockVolume) {
        StockExchangeModel record = getLatestExchangRecord(stockCode);
        if (record == null) {
            record = new StockExchangeModel(stockCode, stockName, stockPrice);
        } else {
            record = new StockExchangeModel(record);
        }

        Log.d(TAG, "sell: latest=" + record);
        record.exchangeNum = stockVolume;
        record.exchangeFee = getFeeOfSell(stockCode, stockPrice, stockVolume);
        record.price = stockPrice;
        if (record.volume == stockVolume) {
            record.profit = stockPrice * stockVolume - record.cost * stockVolume - record.exchangeFee;
            record.cost = 0;
            record.volume = 0;
        } else {
            record.cost = (record.cost * record.volume - stockPrice * stockVolume + record.exchangeFee) / (record.volume - stockVolume);
            record.volume -= stockVolume;
            record.profit = record.price * record.volume - record.cost * record.volume;
        }
        record.capitalization = record.price * record.volume;
        record.exchangeType = StockExchangeModel.SELL;
        record.exchangeTime = System.currentTimeMillis();

        Log.d(TAG, "after sell: " + record);
        writeStockExchangeRecord(record);
        return record;
    }

    //////////////////////////////////////////////Analyse////////////////////////////////////////////////////////
    public AnalyseModel getAnalyseResult() {
        AnalyseModel analyseModel = new AnalyseModel();

        List<StockExchangeModel> latestRecords = getLatestStockExchanges();

        if (latestRecords != null && latestRecords.size() > 0) {

            for (StockExchangeModel exchangeModel : latestRecords) {

                List<StockExchangeModel> records = getStockExchanges(exchangeModel.code);

                if (records != null && records.size() > 0) {
                    for (StockExchangeModel record : records) {
                        if (StockExchangeModel.BUY.equals(record.exchangeType)) {
                            analyseModel.totalBuy += record.price * record.exchangeNum;
                            analyseModel.totalBuyFee += record.exchangeFee;
                        } else {
                            analyseModel.totalSell += record.price * record.exchangeNum;
                            analyseModel.totalSellFee += record.exchangeFee;
                        }
                    }
                }

                analyseModel.totalCapitalization += exchangeModel.capitalization;
                analyseModel.totalProfit += exchangeModel.profit;
            }

        }

        return analyseModel;
    }

}
